/*
 * Copyright (C) 2011, Pavel Samko <bulldozerbsg@gmail.com>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the "Pavel Samko" nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

#include "VCModSpeexDec.h"

VCModSpeexDec::VCModSpeexDec(VCIController & controller)
: VCIMod(controller)
, m_framesize(0)
, m_state(WORK_IDLE)
, m_dec_state(NULL)
{
	speex_bits_init(&m_bits);
	controller.OnNotifyFromMod(*this, VC_INFO_INIT);
}

VCModSpeexDec::~VCModSpeexDec()
{
	speex_bits_destroy(&m_bits);
	if (m_dec_state)
	{
		speex_decoder_destroy(m_dec_state);
		m_dec_state = NULL;
	}
}

VC_RESULT VCModSpeexDec::Push(VC_CMD cmd, void * data_buff, size_t data_size, VC_DATAFORMAT data_format)
{
	VC_RESULT result = VC_FALSE;
	if (data_format == VC_SPEEX)
	{
		switch(cmd)
		{
		case VC_STREAM_BEGIN:
			if ((m_state == WORK_IDLE) && !m_dec_state)
			{
				int iparam;
				
				m_dec_state = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_UWB));
				speex_decoder_ctl(m_dec_state, SPEEX_RESET_STATE, NULL);
				// sampling rate
				speex_decoder_ctl(m_dec_state, SPEEX_GET_SAMPLING_RATE, &iparam);
				if (iparam != 32000)
				{
					iparam = 32000;
					speex_decoder_ctl(m_dec_state, SPEEX_SET_SAMPLING_RATE, &iparam);
				}
				// frame size
				speex_decoder_ctl(m_dec_state, SPEEX_GET_FRAME_SIZE, &m_framesize);
				m_framesize *= sizeof(short);

				m_buff_in.Clear();
				m_buff_out.Clear();

				m_buff_in.Push(data_buff, data_size);
				m_buff_out.PushZero(m_framesize);

				if (m_reciver)
				{
					m_reciver->Push(VC_STREAM_BEGIN, NULL, 0, VC_PCM_1_32000_S16);
				}

				m_controller.OnNotifyFromMod(*this, VC_INFO_STREAM_BEGIN);
				m_state = WORK_STREAM;
				result = VC_OK;

				while (m_buff_in.Size() >= sizeof(long))
				{
					long stream_data_size = *((long *)m_buff_in.GetDataPtr());
					if (stream_data_size + sizeof(long) >= m_buff_in.Size())
					{
						char * buff_in = (char *)m_buff_in.GetDataPtr();
						char * buff_out = (char *)m_buff_out.GetDataPtr();
						if (buff_in && buff_out)
						{
							speex_bits_reset(&m_bits);
							speex_bits_read_from(&m_bits, buff_in + sizeof(long), stream_data_size);
							if (speex_decode_int(m_dec_state, &m_bits, (spx_int16_t *)buff_out) == 0)
							{
								if (m_reciver)
								{
									m_reciver->Push(VC_STREAM, buff_out, m_framesize, VC_PCM_1_32000_S16);
								}
							}
							m_buff_in.PopSize(stream_data_size + sizeof(long));
						} else
						{
							m_controller.OnNotifyFromMod(*this, VC_ERROR);
							break; // error !!!
						}
					} else
					{
						break;
					}
				}
			}
			break;
		case VC_STREAM:
			if ((m_state == WORK_STREAM) && m_dec_state && data_buff && data_size)
			{
				m_buff_in.Push(data_buff, data_size);
				while (m_buff_in.Size() >= sizeof(long))
				{
					long stream_data_size = *((long *)m_buff_in.GetDataPtr());
					if (stream_data_size + sizeof(long) >= m_buff_in.Size())
					{
						char * buff_in = (char *)m_buff_in.GetDataPtr();
						char * buff_out = (char *)m_buff_out.GetDataPtr();
						if (buff_in && buff_out)
						{
							speex_bits_reset(&m_bits);
							speex_bits_read_from(&m_bits, buff_in + sizeof(long), stream_data_size);
							if (speex_decode_int(m_dec_state, &m_bits, (spx_int16_t *)buff_out) == 0)
							{
								if (m_reciver)
								{
									m_reciver->Push(VC_STREAM, buff_out, m_framesize, VC_PCM_1_32000_S16);
								}
							}
							m_buff_in.PopSize(stream_data_size + sizeof(long));
						} else
						{
							m_controller.OnNotifyFromMod(*this, VC_ERROR);
							break; // error !!!
						}
					} else
					{
						break;
					}
				}
				result = VC_OK;
			}
			break;
		case VC_STREAM_END:
			if ((m_state == WORK_STREAM) && m_dec_state)
			{
				if (data_buff && data_size)
				{
					m_buff_in.Push(data_buff, data_size);
				}

				while (m_buff_in.Size() >= sizeof(long))
				{
					long stream_data_size = *((long *)m_buff_in.GetDataPtr());
					if (stream_data_size + sizeof(long) >= m_buff_in.Size())
					{
						char * buff_in = (char *)m_buff_in.GetDataPtr();
						char * buff_out = (char *)m_buff_out.GetDataPtr();
						if (buff_in && buff_out)
						{
							speex_bits_reset(&m_bits);
							speex_bits_read_from(&m_bits, buff_in + sizeof(long), stream_data_size);
							if (speex_decode_int(m_dec_state, &m_bits, (spx_int16_t *)buff_out) == 0)
							{
								if (m_reciver)
								{
									m_reciver->Push(VC_STREAM, buff_out, m_framesize, VC_PCM_1_32000_S16);
								}
							}
							m_buff_in.PopSize(stream_data_size + sizeof(long));
						} else
						{
							m_controller.OnNotifyFromMod(*this, VC_ERROR);
							break; // error !!!
						}
					} else
					{
						break;
					}
				}

				if (m_reciver)
				{
					m_reciver->Push(VC_STREAM_END, NULL, 0, VC_PCM_1_32000_S16);
				}
				
				m_buff_in.Clear();
				m_buff_out.Clear();

				speex_decoder_destroy(m_dec_state);
				m_dec_state = NULL;

				m_controller.OnNotifyFromMod(*this, VC_INFO_STREAM_END);
				m_state = WORK_IDLE;
				result = VC_OK;
			}
			break;
		default:
			break;
		}
	} else
	{
		if (m_reciver)
		{
			result = m_reciver->Push(cmd, data_buff, data_size, data_format);
		}
	}
	return result;
}

